from PyQt4 import QtCore
from PyQt4.QtGui import QMainWindow, QSizePolicy, QDialog, QCursor, QKeySequence, QFileDialog, QVBoxLayout
from S3D.Tools.Sound.EventSearcher.UI.MainWindow import Ui_MainWindow
from S3D.Tools.Sound.EventSearcher.Worker import Worker
from S3D.Tools.Sound.EventSearcher.Updater import *
from S3D.Tools.Sound.EventSearcher.QueryWidgets import QUERY_WIDGETS
from S3D.Tools.Sound.EventSearcher.AddQueryDialog import AddQueryDialog

TITLE_ICON      = QtHelper.loadIcon("EventSearcher.ico")

DEFAULT_NAME_PARAMETERS = (
    EVENT_PROP_PROJECT,
    EVENT_PROP_PATH,
    EVENT_PROP_SOUNDBANK,
    EVENT_PROP_LEVELS,
)

DEFAULT_PARAM_PARAMETERS = (
    EVENT_PROP_PROJECT,
    EVENT_PROP_PATH,
    EVENT_PROP_SEARCH_PARAM,
)

DEFAULT_CUSTOM_PARAMETERS = (
    EVENT_PROP_PROJECT,
    EVENT_PROP_PATH,
    EVENT_PROP_SEARCH_PARAM,
    EVENT_PROP_LEVELS,
)

class ResultsBuilder:
    STYLE_SHEET = """
        .name {
            margin-top: 0.8em;
            margin-bottom: 0;
            font-face: verdana;
            font-size: 14px;
            font-weight: bold
        }
        .property {
            margin-left: 2.5em;
            margin-top: 0.3em;
            margin-bottom: 0;
            width: 120px;
            font-face: verdana;
            font-size: 8em;
            font-weight: bold
        }
        .value {
            margin-left: 0.5em;
            margin-top: 0.3em;
            margin-bottom: 0;
            font-face: verdana;
            font-size: 8em;
        }
    """

    def __init__(self):
        self.recordsCount = 0
        self.lines = []

    def addRecord(self, name, info):
        self.recordsCount += 1

        self.lines.append('<p class="name">%s</p>' % name)

        self.lines.append('<table>')
        for key, val in info:
            self.lines.append('<tr><td><p class="property">%s:</p></td><td><p class="value">%s</p></td></tr>' % (key, val))
        self.lines.append('</table>')

    def getText(self):
        return '\n'.join(self.lines)

class MainWindow(QMainWindow):
    def __init__(self, config):
        super(MainWindow, self).__init__()

        self.cfg = config

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.setWindowIcon(TITLE_ICON)

        # TODO: restore previous tasks
        self.cfg.customTasks = []
        self.taskWidgets = []
        
        self.initUi()

        self.worker = Worker(self.cfg.workspace)
        self.worker.start()

        self.show()

        self.startTimer(100)
        self.statusBar().showMessage("Ready")

        self.ui.searchNameEdit.setFocus()

    def initUi(self):
        QtHelper.initWorkspace(self.ui.workspaceBox, self.cfg, onChange=self.onWorkspaceChanged)

        self.ui.propertyBox.addItems([propName for propName, _ in Sound.EVENT_PROPERTIES])

        self.dataBind   = QtHelper.DataBind()
        self.searchName = ""
        self.dataBind.bindText(self.ui.searchNameEdit,      self,       "searchName",       onChanged=self.onSearchChanged)
        self.dataBind.bindCheck(self.ui.searchSubNameCheck, self.cfg,   "searchSubNames",   onChanged=self.onSearchSubNameChanged)

        self.dataBind.bindIndex(self.ui.propertyBox,        self.cfg,   "rangeParam",       onChanged=self.onRangeParameterChanged)
        self.dataBind.bindValue(self.ui.rangeFromValue,     self.cfg,   "rangeFrom",        onChanged=self.onPropertyRangeChanged)
        self.dataBind.bindValue(self.ui.rangeToValue,       self.cfg,   "rangeTo",          onChanged=self.onPropertyRangeChanged)
        self.dataBind.bindCheck(self.ui.enableRangeCheck,   self.cfg,   "rangeToEnable",    onChanged=self.onPropertyRangeChanged)
        self.dataBind.updateValues()

        self.ui.resultsView.document().setDefaultStyleSheet(ResultsBuilder.STYLE_SHEET)

        # route key events
        self.ui.resultsView.keyPressEvent   = self.keyPressEvent

        self.customLayout = QVBoxLayout()
        self.customLayout.addItem(QtGui.QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.ui.customAreaWidget.setLayout(self.customLayout)

        self.updateCurrentTabHeight()

    def onSettingsClicked(self):
        projects    = self.cfg.selectedProjects.get(self.cfg.workspace)

        searchTypeIndex = self.ui.tabWidget.currentIndex()
        parameters  = self.cfg.selectedParameters[searchTypeIndex]

        dlg = SettingsDialog(self, projects, parameters, self.cfg.lastSettingsTab)
        res = dlg.exec_()

        self.cfg.lastSettingsTab = dlg.lastSettingsTab

        if res != QDialog.Accepted:
            return

        self.cfg.selectedProjects[self.cfg.workspace] = dlg.selectedProjects
        self.cfg.selectedParameters[searchTypeIndex]  = dlg.selectedParameters

        self.querySearch()

    def onResultsContextMenu(self, pos):
        menu = self.ui.resultsView.createStandardContextMenu()
        action = menu.addAction("Save As...")
        action.setShortcut(QKeySequence("Ctrl+S"))
        QtCore.QObject.connect(action, QtCore.SIGNAL("triggered()"), self.onSaveResults)
        menu.exec_(QCursor().pos())

    def onSaveResults(self):
        path = QFileDialog().getSaveFileName(self,
            "Save Search Results",
            Sound.getSourcePath(),
            "CSV files (*.csv);;Text file (*.txt);;Listing file (*.lst)")
        path = unicode(path)

        if not path:
            return

        self.querySearch(path)

    def querySearch(self, path=None):
        currentTab = self.ui.tabWidget.currentWidget()
        if currentTab is self.ui.nameTab:
            type, data = self.getNameSearchData()
        elif currentTab is self.ui.propertyTab:
            type, data = self.getParamSearchData()
        elif currentTab is self.ui.customTab:
            type, data = self.getCustomSearchData()
        else:
            raise AssertionError("Unknown search tab")

        projects = self.getSearchProjects()

        if path is None:
            self.worker.sendMessage("search", type, projects, data)
        else:
            paramList = self.getResultParameters(type, data)
            self.worker.sendMessage("save", path, paramList, type, projects, data)

    def getSearchProjects(self):
        try:
            return self.cfg.selectedProjects[self.cfg.workspace]
        except KeyError:
            pass

        return Sound.getProjectsList()

    def getNameSearchData(self):
        searchType = self.cfg.searchSubNames and "subName" or "name"
        return searchType, self.searchName

    def getParamSearchData(self):
        rangeFrom       = self.cfg.rangeFrom
        rangeToEnable   = self.cfg.rangeToEnable
        rangeTo         = self.cfg.rangeTo if rangeToEnable else rangeFrom

        data = self.cfg.rangeParam, rangeFrom, rangeTo
        return "param", data

    def getCustomSearchData(self):
        for task, widget in zip(self.cfg.customTasks, self.taskWidgets):
            data = widget.getData()
            task.setData(*data)

        return "custom", self.cfg.customTasks

    def onSearchChanged(self, value=None):
        if not self.searchName:
            self.ui.resultsView.setHtml("")
            self.statusBar().showMessage("Ready")
            self.worker.sendMessage("search", None, None, None) # notify that last search was empty
            return

        self.querySearch()

    def onRangeParameterChanged(self, value=None):
        try:
            self.cfg.rangeFrom, self.cfg.rangeTo, self.cfg.rangeToEnable = self.cfg.rangeParamsCache[self.cfg.rangeParam]
            self.dataBind.updateValues()
        except KeyError:
            pass

        self.onPropertyRangeChanged()

    def onPropertyRangeChanged(self, value=None):
        self.cfg.rangeParamsCache[self.cfg.rangeParam] = self.cfg.rangeFrom, self.cfg.rangeTo, self.cfg.rangeToEnable

        self.querySearch()

    def updateCurrentTabHeight(self):
        currentIndex = self.ui.tabWidget.currentIndex()

        if currentIndex == 2:
            self.ui.tabWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        else:
            self.ui.tabWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)

        for index in range(self.ui.tabWidget.count()):
            vertPolicy = QSizePolicy.Ignored
            if index == currentIndex:
                vertPolicy = QSizePolicy.Minimum

            w = self.ui.tabWidget.widget(index)
            w.setSizePolicy(QSizePolicy.Expanding, vertPolicy)

    def onCurrentTabChanged(self, index):
        self.updateCurrentTabHeight()
        self.querySearch()

    def onSearchSubNameChanged(self, value):
        self.onSearchChanged()

    def onWorkspaceChanged(self, workspace):
        self.worker.sendMessage("workspace", workspace)
        Workspace.setWorkspace(workspace)

    def keyPressEvent(self, e):
        if self.ui.tabWidget.currentWidget() is not self.ui.nameTab:
            return

        self.ui.searchNameEdit.keyPressEvent(e)

    def closeEvent(self, e):
        self.worker.sendMessage("stop")

    def timerEvent(self, e):
        while self.worker.hasReply():
            message, data = self.worker.getReply()
            if message == "status":
                self.statusBar().showMessage(data)
            elif message == "search_result":
                self.updateResults(*data)
            else:
                print("Searcher: Unknown message: %s" % message)

    def getResultParameters(self, type, data):
        if type in ("name", "subName"):
            selected = set(self.cfg.selectedParameters[0])
        elif type == "param":
            selected = set(self.cfg.selectedParameters[1])

            paramIndex = data[0]
            if EVENT_PROP_SEARCH_PARAM in selected:
                selected.add(EVENT_PROP_PARAM + paramIndex)
        elif type == "custom":
            selected = set(self.cfg.selectedParameters[2])

            if EVENT_PROP_SEARCH_PARAM in selected:
                for p in data:
                    selected.update(p.getAutoParams())
        elif type is None:
            return []
        else:
            raise AssertionError("Unknown search type: %s" % type)

        # don't include automatic properties
        for index in selected & EVENT_AUTOMATIC_PROPS:
            selected.remove(index)

        return list(sorted(selected))

    def updateResults(self, type, data, events):
        res = ResultsBuilder()

        paramList = self.getResultParameters(type, data)

        for p in events:
            infoList = [(EVENT_PROP_DESC[index], p.getProp(index, PROP_CONTEXT_SHOW)) for index in paramList]
            res.addRecord(p.name, infoList)

        self.ui.resultsView.setHtml(res.getText())

    def onAddQuery(self):
        dlg = AddQueryDialog(self)
        if dlg.exec_() != QDialog.Accepted:
            return

        task = dlg.selectedType()
        widgetType, widgetData = task.getDesc()

        try:
            widget = QUERY_WIDGETS[widgetType](*widgetData)
        except KeyError:
            print("Unknown widget type: %s" % widgetType)
            return

        QtCore.QObject.connect(widget, QtCore.SIGNAL("changed()"), self.querySearch)
        QtCore.QObject.connect(widget, QtCore.SIGNAL("remove()"),  lambda: self.onRemoveWidget(widget))

        self.ui.customAreaWidget.hide()
        self.customLayout.insertWidget(self.customLayout.count() - 1, widget)
        self.ui.customAreaWidget.show()

        self.cfg.customTasks.append(task)
        self.taskWidgets.append(widget)
        self.querySearch()

    def onRemoveWidget(self, widget):
        index = self.taskWidgets.index(widget)

        self.ui.customAreaWidget.hide()
        self.customLayout.takeAt(index)
        widget.close()
        self.ui.customAreaWidget.show()

        del self.cfg.customTasks[index]
        del self.taskWidgets[index]
        self.querySearch()

    def onRemoveQuery(self):
        self.ui.customAreaWidget.hide()
        while True:
            item = self.customLayout.takeAt(0)
            if item is None:
                break

            widget = item.widget()
            if widget: widget.close()

        self.customLayout.addItem(QtGui.QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.ui.customAreaWidget.show()

        self.cfg.customTasks = []
        self.taskWidgets = []
        self.querySearch()
